//
// Created by Suzhen on 2017/2/27.
//

#include "SocketAcceptor.h"
#include "SocketIO.h"
#include "Utils.h"

USING_ORION_V2


//########################################SocketAcceptor##################################################

SocketAcceptor::~SocketAcceptor() {

}

SocketAcceptor::SocketAcceptor(Configuration &&cnf,
                               const IOBehavior &iob,
                               TlsConf &&tls)
        : FileStream(0, IOBehavior(), 0),
          configurations_(std::forward<Configuration>(cnf)),
          userIob_(iob),
          tlsConf_(std::forward<TlsConf>(tls)) {
}

void SocketAcceptor::setCustomAcceptorIOBehavior(const IOBehavior &iob) {
    this->ioBehavior_ = iob;
}

void SocketAcceptor::onStreamIO(int status,
                                EventFlag) {
    SYS_FAULT(status == 0) << "SocketAcceptor , onStreamIO status = 0";
    //todo 这里可能也需要检测 status 和 ioe
    struct sockaddr_in inAddr;
    socklen_t inAddrLen = sizeof inAddr;
    uv_os_sock_t inSockFd = ::accept(coreAsyncPollData.fd, (struct sockaddr *) &inAddr, &inAddrLen);
#ifdef OSX
    //todo linux上,是否也需要相同的处理?
    //set SO_NOSIGPIPE
    int noSigpipe = 1;
    setsockopt(inSockFd, SOL_SOCKET, OPT_NOT_SIG_PIPE, &noSigpipe, sizeof(int));
#endif
    //创建一个异步SocketStream
    SocketIO *socketIo =
            SocketIO::CreateSocket(inSockFd,
                                   userIob_,
                                   configurations_.socketIdleMs,
                                   tlsConf_.isSslCtxValid() ? SSL_new(tlsConf_.getNativeSslCtx()) : NULL);
    userIob_.notifyOnAccept(socketIo);
}

void SocketAcceptor::attach(AsyncEventGenerator *gen) {
    int r;
    FdType acceptorFd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    SYS_FAULT(acceptorFd >= 0) << "listener socket create failed";
    r = ::setsockopt(acceptorFd,
                     SOL_SOCKET,
                     SO_REUSEADDR,
                     &configurations_.allowReuseAddr,
                     sizeof configurations_.allowReuseAddr);
    SYS_FAULT(r == 0) << "setsockopt failed --> allowReuseAddr val = "
                      << configurations_.allowReuseAddr;
    struct sockaddr_in bindSockAddr;
    Utils::InitSockAddr(configurations_.bindAddr, configurations_.bindPort, bindSockAddr);
    r = ::bind(acceptorFd, (const struct sockaddr *) &bindSockAddr, sizeof bindSockAddr);
    SYS_FAULT(r == 0) << "bind failed , errno = " << errno;
    //重要: 设置接收器的文件描述符
    coreAsyncPollData.fd = acceptorFd;
    //
    r = ::listen(coreAsyncPollData.fd, (int) configurations_.maxBackLog);
    CHECK(r == 0) << "listen error , errno is -> " << errno;
    FileStream::attach(gen);
}

//########################################SocketAcceptor##################################################
